﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using Antix.Mail.Smtp.Impostor;
using Xunit;

namespace Antix.Mail.Tests
{
    ///<summary>
    ///  This is a test class for FileMessageStorageTest and is intended
    ///  to contain all FileMessageStorageTest Unit Tests
    ///</summary>
    public class FileMessageStorageTests
    {
        const int MESSAGE_COUNT = 10;

        ///<summary>
        ///  A test for Retrieve
        ///</summary>
        [Fact]
        public void RetrieveTest()
        {
            var dropPath = Path.Combine(Path.GetTempPath(), "RetrieveTest");
            if (Directory.Exists(dropPath)) Directory.Delete(dropPath, true);

            using (var server = new Server())
            {
                var host = server.CreateHost(new HostConfiguration
                {
                    MessageStorage = new FileMessageStorageConfiguration
                    {
                        DropPath = dropPath
                    }
                });
                var messageStorage = host.Messages;
                host.Start();

                ImpostorTests.SendEmails(server, MESSAGE_COUNT);

                Assert.Equal(MESSAGE_COUNT, messageStorage.Count);

                var firstId = messageStorage.First().Id;
                var message = messageStorage.Retrieve(firstId);

                Assert.NotNull(message);
                Assert.NotNull(message.Id);
                Assert.NotNull(message.From);
                Assert.NotNull(message.To);
                Assert.NotNull(message.Subject);
                Assert.NotNull(message.Headers);
                Assert.NotNull(message.Data);
            }
        }

        ///<summary>
        ///  A test for Store
        ///</summary>
        [Fact]
        public void StoreTest()
        {
            var dropPath = Path.Combine(Path.GetTempPath(), "StoreTest");
            if (Directory.Exists(dropPath)) Directory.Delete(dropPath, true);

            var expectedId = Guid.NewGuid().ToString();
            var expectedPath = Path.Combine(dropPath, $"{expectedId}.xxx");

            using (var storage = new FileMessageStorageConfiguration
            {
                DropPath = dropPath,
                FileExtension = ".xxx"
            }.Create(new HostConfiguration()))
            {
                var addedItems = new List<MessageInfo>();
                storage.CollectionChanged += (sender, e)
                    => addedItems.AddRange(e.NewItems.Cast<MessageInfo>());

                storage.Store(new Message {Id = expectedId});

                Assert.True(File.Exists(expectedPath),
                    $"expected path {expectedPath} not found");

                Assert.NotNull(addedItems);
                Assert.Equal(1, addedItems.Count());
                Assert.Equal(expectedId, addedItems.First().Id);
            }
        }

        ///<summary>
        ///  A test for GetEnumerator
        ///</summary>
        [Fact]
        public void GetEnumeratorTest()
        {
            const int port = 25;

            var dropPath = Path.Combine(Path.GetTempPath(), "GetEnumeratorTest");
            if (Directory.Exists(dropPath)) Directory.Delete(dropPath, true);

            using (var server = new Server())
            {
                var host = server.CreateHost(new HostConfiguration
                {
                    Port = port,
                    MessageStorage = new FileMessageStorageConfiguration
                    {
                        DropPath = dropPath
                    }
                });
                host.Start();

                ImpostorTests.SendEmails(server, MESSAGE_COUNT);

                Assert.Equal(MESSAGE_COUNT, host.Messages.Count);
                foreach (var info in host.Messages)
                {
                    Assert.NotNull(info.Subject);
                }
            }
        }

        [Fact]
        public void DeleteTest()
        {
            const int port = 25;

            var dropPath = Path.Combine(Path.GetTempPath(), "DeleteTest");
            if (Directory.Exists(dropPath)) Directory.Delete(dropPath, true);

            using (var server = new Server())
            {
                var host = server.CreateHost(new HostConfiguration
                {
                    Port = port,
                    MessageStorage = new FileMessageStorageConfiguration
                    {
                        DropPath = dropPath
                    }
                });
                host.Start();

                ImpostorTests.SendEmails(server, MESSAGE_COUNT);

                Assert.Equal(MESSAGE_COUNT, host.Messages.Count);
                var firstId = host.Messages.First().Id;
                host.Messages.Delete(firstId);
                Assert.Equal(MESSAGE_COUNT - 1, host.Messages.Count);

                Assert.False(host.Messages.Contains(firstId));
            }
        }

        [Fact]
        public void DeleteFileTest()
        {
            const int port = 25;

            var dropPath = Path.Combine(Path.GetTempPath(), "DeleteTest");
            var fileExtension = ".xxx";
            if (Directory.Exists(dropPath)) Directory.Delete(dropPath, true);

            using (var server = new Server())
            {
                var host = server.CreateHost(new HostConfiguration
                {
                    Port = port,
                    MessageStorage = new FileMessageStorageConfiguration
                    {
                        DropPath = dropPath,
                        FileExtension = fileExtension
                    }
                });
                host.Start();

                ImpostorTests.SendEmails(server, MESSAGE_COUNT);

                Assert.Equal(MESSAGE_COUNT, host.Messages.Count);
                var firstId = host.Messages.First().Id;
                File.Delete(Path.Combine(dropPath, string.Concat(firstId, fileExtension)));
                Thread.Sleep(1000); // give time for the watcher to catch up

                Assert.Equal(MESSAGE_COUNT - 1, host.Messages.Count);

                Assert.False(host.Messages.Any(m => m.Id.Equals(firstId)));
            }
        }

        [Fact]
        public void DeleteAllTest()
        {
            const int port = 25;

            var dropPath = Path.Combine(Path.GetTempPath(), "DeleteAllTest");
            if (Directory.Exists(dropPath)) Directory.Delete(dropPath, true);

            using (var server = new Server())
            {
                var host = server.CreateHost(new HostConfiguration
                {
                    Port = port,
                    MessageStorage = new FileMessageStorageConfiguration
                    {
                        DropPath = dropPath
                    }
                });
                host.Start();

                ImpostorTests.SendEmails(server, MESSAGE_COUNT);

                Assert.Equal(MESSAGE_COUNT, host.Messages.Count);
                host.Messages.DeleteAll();
                Assert.Equal(0, host.Messages.Count);
            }
        }
    }
}